﻿
/**
  * @file linux_thread_impl.h
  * < windows thread warpped >
  *
  * Copyright (c) by <lid/>
  * @author jingwei.guan@hotmail.com
  * modify history:
  * date     version     author     content
  * 1/8/2011  0.0.1       lid       thread have to provide create, start, end,
									run_delegate, done_delegate, notice_container_delegate function for container policy
 */


#define LinuxThread_Template_Params    Make_Params1(LinuxThread_Num_Args, class T) // class T0, class T1, class T2, ...
#define LinuxThread_Template_Args      Make_Params1(LinuxThread_Num_Args, T) // T0, T1, T2, ...
#define LinuxThread_Function_Params    Make_Params2(LinuxThread_Num_Args, T, a) // T0 a0, T1 a1, T2 a2, ...
#define LinuxThread_Function_Args      Make_Params1(LinuxThread_Num_Args, a) // a0, a1, a2, ...

#undef LinuxThread_Comma

#if LinuxThread_Num_Args == 0
	#define LinuxThread_Comma
#else
	#define LinuxThread_Comma ,
#endif

namespace thread
{
	template <typename R LinuxThread_Comma LinuxThread_Template_Params>
	class LinuxThread<R (LinuxThread_Template_Args)>
	{
	public:
		typedef LinuxThread<R (LinuxThread_Template_Args)> self;
		typedef typename delegate::Delegate<R (LinuxThread_Function_Params)> run_delegate_type;
		typedef typename delegate::Delegate<void (self*)> notice_container_delegate_type;
		typedef typename delegate::Delegate<void (int32_t)> notice_user_delegate_type;
		// thread invoke content
		run_delegate_type			   run_delegate;
		// notice user thread invoke is end
		notice_user_delegate_type	   done_delegate;
		// notice containter thread invoke is end
		notice_container_delegate_type notice_container_delegate;
	public:
		LinuxThread()
			:isStop_(false)
		{
		}
		~LinuxThread()
		{
			void* ret = NULL;
			pthread_join(tid_,&ret);
			sem_destroy(&wait_);
		}
		bool create()
		{
			int32_t ret = sem_init(&wait_,0,0);
			if(0!=ret)
			{
				return false;
			}
			ret = pthread_create(&tid_,NULL,_threadProc,(void*)this);
			return 0==ret? true:false;
		}
		void start(LinuxThread_Function_Params)
		{
#if LinuxThread_Num_Args == 0
#else
			arguments_.copy(LinuxThread_Function_Args);
#endif
			_setEvent();
		}
		void stop()
		{
			isStop_ = true;
			_setEvent();
		}
	protected:
		LinuxThread( const self& );
		LinuxThread& operator = ( const self& );
		void _setEvent()
		{
		    int sval = _sem_sval();
			if (sval<=0)
			{
                sem_post(&wait_);
			}
		}
		inline int32_t _sem_sval()
		{
			int32_t sval = 0;
			sem_getvalue(&wait_,&sval);
			return sval;
		}
		static void* _threadProc(void* param)
		{
			self* t = (self*)param;
			t->_actualThreadProc();
			pthread_exit(0);
			return NULL;
		}
		void _actualThreadProc()
		{
			while(!isStop_)
			{
				sem_wait(&wait_);
				if (isStop_)
				{
					return;
				}
#if LinuxThread_Num_Args == 0
				run_delegate();
#elif LinuxThread_Num_Args == 1
				run_delegate(arguments_.a);
#elif LinuxThread_Num_Args == 2
				run_delegate(arguments_.a,arguments_.b);
#elif LinuxThread_Num_Args == 3
				run_delegate(arguments_.a,arguments_.b,arguments_.c);
#elif LinuxThread_Num_Args == 4
				run_delegate(arguments_.a,arguments_.b,arguments_.c,arguments_.d);
#elif LinuxThread_Num_Args == 5
				run_delegate(arguments_.a,arguments_.b,arguments_.c,arguments_.d,arguments_.e);
#elif LinuxThread_Num_Args == 6
				run_delegate(arguments_.a,arguments_.b,arguments_.c,arguments_.d,arguments_.e,arguments_.f);
#elif LinuxThread_Num_Args == 7
				run_delegate(arguments_.a,arguments_.b,arguments_.c,arguments_.d,arguments_.e,arguments_.f,arguments_.g);
#elif LinuxThread_Num_Args == 8
				run_delegate(arguments_.a,arguments_.b,arguments_.c,arguments_.d,arguments_.e,arguments_.f,arguments_.g,arguments_.h);
#endif

				run_delegate.clean();

				// notice and clear user
				done_delegate(run_end);
				done_delegate.clean();

				// for container
				notice_container_delegate(this);
			}
		}
	private:
		enum
		{
			init = 0,
			run_prepare,
			running,
			run_end, // run  end
			end,     // thread end
		};
	private:
		pthread_t tid_;
		sem_t wait_;
		bool isStop_;
#if LinuxThread_Num_Args == 0
#else
		tuple::Tuple<LinuxThread_Template_Args> arguments_;
#endif
	};
}
